import configparser
import lmdb
import cherrypy
import pickle
import chevron
from hashlib import sha256
from logger import Logger

# main.py will run golestan_reporter and any other services
from golestan_reporter import GolestanReporter
import os

T = 'main'
CONFIG_FILE_DIR = 'config.ini'
CONFIG_FILE_ENCODER = 'utf8'
DEBUG = True
DEV_PASS = 'eda57e1df3f6fb8a9ac094b95fc9cfb20d4783db8ecc8261f232f606fe35cbe3'       # hint: adamsmozi

class TemplatesCache:
    def __init__(self, templates_dir, cache):
        self.cache = cache
        get_path = lambda x: os.path.join(templates_dir, x)
        self.__templates = {
            'signin' : get_path('signin.html'),
            'signup' : get_path('signup.html')
        }
        if cache:
            for k, v in self.__templates:
                with open(v) as f:
                    self.__templates[k]=f.read()
    
    def get_template(self, template_key):
        if self.cache:
            return self.__templates[template_key]
        else:
            with open(self.__templates[template_key]) as template_file:
                template = template_file.read()
            return template

@cherrypy.tools.register('before_handler')
def auth(class_):
    s = cherrypy.session
    if class_=='all' and s.get('user class') is not None:
        return
    elif s.get('user class') in class_:
        return
    else:
        raise cherrypy.HTTPRedirect('/signin')

class Root:
    def __init__(self, env, users_db, config: configparser.SectionProxy, logger: Logger, templates):
        self.templates = templates
        self.env = env
        self.usersDB = users_db
        self.cfg = config
        self.l = logger
        self.get_hashsum = lambda x: sha256(x.encode()).hexdigest()


    def get_user_data(self, user, key = None):
        self.l.d(f'getting user({user}) info',T)
        try:
            with self.env.begin(self.usersDB) as txn:
                data = txn.get(user.enconde())
            user_info = pickle.loads(data)
            if key:
                return user_info[key]
            return user_info
        except Exception as e:
            self.l.e('error on getting user info',T)
            self.l.d(f'{type(e)}: {e}',T)

    def set_user_data(self, user, value, key=None):
        self.l.i(f'setting user({user}) info',T)
        self.l.d(f'key:{key}'+', value:{value}'if key else ', value is full user info',T)
        try:
            if key:
                value_ = value
                value = self.get_user_data(user)
                value[key] = value_
            data = pickle.dumps(value)
            with self.env.begin(self.usersDB,write=True) as txn:
                txn.put(key.enconde(), data)
        except Exception as e:
            self.l.e('error on setting user info',T)
            self.l.d(f'{type(e)}: {e}',T)

    @cherrypy.expose
    def signup(self, **info):
        pass

    @cherrypy.expose
    def verify(self, q):
        pass

    @cherrypy.expose
    def signin(self, username=None, password=None):
        if username is None or password is None:
            # signin form
            return self.templates.get_template('signin')
        else:
            # do signin:
            if username == 'admin':
                if self.get_hashsum(password)==self.cfg['admin_pass_sha256']:
                    cherrypy.session['user class']='admin'
                    cherrypy.session['login']=True
                    raise cherrypy.HTTPRedirect('/admincp')
                else:
                    return 'incorrect password'
            elif username == 'developer' and DEBUG:
                if self.get_hashsum(password)==DEV_PASS:
                    cherrypy.session['user class']='dev'
                    cherrypy.session['login']=True
                    raise cherrypy.HTTPRedirect('/devtool')
                    # TODO: add devtool
                else:
                    return 'incorrect password'
            else:
                user_info = self.get_user_data(username)
                if user_info:
                    if self.get_hashsum(password) == user_info['passhash']:
                        cherrypy.session['user class'] = 'user'
                        cherrypy.session['username'] = username
                        cherrypy.session['login']=True
                        raise cherrypy.HTTPRedirect('/cp')
                    else:
                        return 'incorrect password'
                else:
                    return 'incorrect username'

    @cherrypy.expose
    def index(self):
        # no index page at now. this will redirect user to signin
        s = cherrypy.session
        if s.get('user class') == 'admin':
            raise cherrypy.HTTPRedirect('/admincp')
        elif s.get('user class') == 'dev' and DEBUG:
            raise cherrypy.HTTPRedirect('/devtool')
        elif s.get('user class') == 'user':
            raise cherrypy.HTTPRedirect('/cp')
        else:
            raise cherrypy.HTTPRedirect('/signin')

    @cherrypy.auth(('dev','users'))
    def cp(self):
        s = cherrypy.session
        student = None
        if s.get('user class') == 'user':
            if s.get('user') != None:
                student = self.get_user_data(s.get('user'))
            else:
                self.l.w('user class is user but no user attribute found in session',T)
                raise cherrypy.HTTPRedirect('/signin')
        # TODO: add web interface

    @cherrypy.auth(('dev','admin'))
    def admincp(self):
        # TODO: add web interface
        pass


cfg = configparser.ConfigParser()
# TODO: use a dynamic cfg directory given in arg
with open(CONFIG_FILE_DIR, encoding=CONFIG_FILE_ENCODER) as cfgFile:
    cfg.readfp(cfgFile)
maincfg=cfg['Main']
logger = Logger(maincfg.get('log_dir','.log'), maincfg.getint('log_level',0), DEBUG)
templates = TemplatesCache(maincfg.get('templates_dir'),not DEBUG)
with lmdb.open(maincfg.get('db_dir','.db'), max_dbs=2) as env:
    usersDB = env.open_db(maincfg.get('users_database','users').encode())
    golestan_reporter = GolestanReporter(env=env, users_db=usersDB, config=cfg['Golestan reporter'], logger=logger, tempaltes=templates)
    
    # web services:
    root = Root(env, usersDB, maincfg, logger, templates)

    golestan_reporter.start()

    # TODO: run web tools

    cherry_cfg = {
        "global":
            {
                "server.socket_host": maincfg.get('host','0.0.0.0'),
                "server.socket_port": maincfg.getint('port',8080),
                "server.socket_file": maincfg.get('unix_socket'),
                "tools.sessions.on": True,
                "tools.staticdir.on": True,
                "tools.staticdir.dir": os.path.abspath("./webInterface/files/"),
                "tools.staticdir.root": "/",
                "environment": None if DEBUG else "production"
            }
    }

    cherrypy.quickstart(root, '/', cherry_cfg)

    golestan_reporter.join()